#include "gkt_malloc.h"
#include "g_qspi.h"
#include "gkt_flash.h"
#include "gkt_board.h"
#include "gkt_driver.h"

#define FLASH_HEAD_RESERVED_SIZE	\
	(GKT_QSPIFLASH_SYSTEM_RESERVED_SIZE + GKT_CONFIG_CODE_RESERVED_SIZE)

static const gkt_flash_info_s sc_qspi_flash_info = {
	.page_size	= GKT_QSPIFLASH_PAGE_SIZE,
	.sector_size	= GKT_QSPIFLASH_SECTOR_SIZE,
	.block_size	= GKT_QSPIFLASH_BLOCK_32K_SIZE,
	.write_protect_unit_size	= 0,
	.total_size	= GKT_QSPIFLASH_MAXSIZE - FLASH_HEAD_RESERVED_SIZE,
	.permission	= GKT_FLASH_PERM_READABLE | GKT_FLASH_PERM_WRITABLE
					| GKT_FLASH_PERM_ERASABLE,
};

const gkt_flash_info_s *gkt_flash_get_info(uint32_t dev_id)
{
	return (GKT_QSPIFLASH_ID == dev_id) ? &sc_qspi_flash_info : NULL;
}

static int flash_check_base_params(uint32_t dev_id, uint32_t offset, 
			uint32_t size)
{
	if ((GKT_QSPIFLASH_ID == dev_id)
		&& (offset >= FLASH_HEAD_RESERVED_SIZE)
		&& (offset < GKT_QSPIFLASH_MAXSIZE)
		&& (size > 0) 
		&& (size <= (GKT_QSPIFLASH_MAXSIZE - offset)))
	{
		return 1;
	}

	return 0;
}

void *gkt_flash_map(uint32_t dev_id, uint32_t offset, 
			uint32_t size)
{
	gkt_unused(dev_id);
	gkt_unused(offset);
	gkt_unused(size);

	return NULL;
}

int gkt_flash_read(uint32_t dev_id, 
			uint32_t offset, uint32_t size, 
			void *buffer)
{
	G_ErrorStatus err_status;

	if (flash_check_base_params(dev_id, offset, size) && buffer)
	{
		err_status = (G_ErrorStatus)G_qspi_flash_read(offset, buffer, size);
		return (SUCCESS == err_status) ? GKT_SUCCESS : GKT_ELOWLEVEL;
	}

	return GKT_EPARAM;
}

int gkt_flash_write(uint32_t dev_id, 
			uint32_t offset, uint32_t size, 
			const void *buffer)
{
	uint32_t curr_offset, action_size, left_size;
	uint32_t buff_addr;
	G_ErrorStatus err_status;

	if (flash_check_base_params(dev_id, offset, size)
		&& buffer)
	{
		curr_offset = offset;
		left_size = size;
		buff_addr = (uint32_t)buffer;

		action_size = 0;
		do {
			curr_offset += action_size;
			buff_addr += action_size;

			if (curr_offset % GKT_QSPIFLASH_PAGE_SIZE) {
				action_size = GKT_QSPIFLASH_PAGE_SIZE - (curr_offset % GKT_QSPIFLASH_PAGE_SIZE);
				if (action_size > left_size)
					action_size = left_size;
			}
			else if (left_size >= GKT_QSPIFLASH_PAGE_SIZE)
				action_size = left_size - (left_size % GKT_QSPIFLASH_PAGE_SIZE);
			else
				action_size = left_size;

			err_status = (G_ErrorStatus)G_qspi_flash_write(curr_offset,
							(uint8_t *)buff_addr, action_size);
			if (err_status != SUCCESS)
				return GKT_ELOWLEVEL;

			left_size -= action_size;
		} while (left_size > 0);

		return GKT_SUCCESS;
	}

	return GKT_EPARAM;
}

typedef struct _flash_erase_attr	flash_erase_attr_s;
typedef int (*flash_erase_fn)(const flash_erase_attr_s *c_attr, 
				uint32_t offset, uint32_t size);
typedef uint8_t (*flash_erase_unit_fn)(uint32_t offset);
struct _flash_erase_attr {
	uint32_t	unit_size;
	uint32_t	ll_unit_size;
	flash_erase_fn	func;
	flash_erase_unit_fn	ll_unit_func;
};

static uint8_t *s_flash_sector_buffer = NULL;
static int flash_erase_incomplete_sector(
	const flash_erase_attr_s *c_attr,
	uint32_t offset, uint32_t size)
{
	uint32_t aligned_offset;
	G_ErrorStatus err_status;

	if (!s_flash_sector_buffer) {
		s_flash_sector_buffer = (uint8_t *)malloc(GKT_QSPIFLASH_SECTOR_SIZE);
		if (!s_flash_sector_buffer)
			return GKT_ENOMEMORY;
	}

	aligned_offset = offset & (~(GKT_QSPIFLASH_SECTOR_SIZE - 1));
	err_status = (G_ErrorStatus)G_qspi_flash_read(aligned_offset, 
					s_flash_sector_buffer, GKT_QSPIFLASH_SECTOR_SIZE);
	if (err_status != SUCCESS)
		return GKT_ELOWLEVEL;

	memset(&s_flash_sector_buffer[offset - aligned_offset],
		0xff, size);

	err_status = (G_ErrorStatus)G_qspi_flash_sectorerase(aligned_offset);
	if (err_status != SUCCESS)
		return GKT_ELOWLEVEL;

	err_status = (G_ErrorStatus)G_qspi_flash_write(aligned_offset, 
					s_flash_sector_buffer, GKT_QSPIFLASH_SECTOR_SIZE);
	if (err_status != SUCCESS)
		return GKT_ELOWLEVEL;

	return (int)size;
}

static int flash_erase_incomplete_unit(
			const flash_erase_attr_s *c_attr,
			uint32_t offset, uint32_t size)
{
	uint32_t unit_nums, i;
	G_ErrorStatus err_status;

	unit_nums = size / c_attr->ll_unit_size;
	for (i = 0; i < unit_nums; i++) {
		err_status = (*c_attr->ll_unit_func)(offset);
		if (err_status != SUCCESS)
			return GKT_ELOWLEVEL;
		offset += c_attr->ll_unit_size;
	}

	return (int)(unit_nums * c_attr->ll_unit_size);
}


enum {
	FLASH_ERASE_INCOMPLETE_SECTOR = 0,
	FLASH_ERASE_INCOMPLETE_BLOCK_32K,
	FLASH_ERASE_INCOMPLETE_BLOCK_64K,
	FLASH_ERASE_BLOCK_64K,
	FLASH_ERASE_MAX
};
static const flash_erase_attr_s sc_flash_erase_attrs[FLASH_ERASE_MAX] = {
	[FLASH_ERASE_INCOMPLETE_SECTOR] = {
		.unit_size	= GKT_QSPIFLASH_SECTOR_SIZE,
		.ll_unit_size	= 1,
		.func	= flash_erase_incomplete_sector,
		.ll_unit_func	= NULL,
	},
	[FLASH_ERASE_INCOMPLETE_BLOCK_32K] = {
		.unit_size	= GKT_QSPIFLASH_BLOCK_32K_SIZE,
		.ll_unit_size	= GKT_QSPIFLASH_SECTOR_SIZE,
		.func	= flash_erase_incomplete_unit,
		.ll_unit_func	= G_qspi_flash_sectorerase,
	},
	[FLASH_ERASE_INCOMPLETE_BLOCK_64K] = {
		.unit_size	= GKT_QSPIFLASH_BLOCK_64K_SIZE,
		.ll_unit_size	= GKT_QSPIFLASH_BLOCK_32K_SIZE,
		.func	= flash_erase_incomplete_unit,
		.ll_unit_func	= G_qspi_flash_blockerase32k,
	},
	[FLASH_ERASE_BLOCK_64K] = {
		.unit_size	= GKT_QSPIFLASH_MAXSIZE,
		.ll_unit_size	= GKT_QSPIFLASH_BLOCK_64K_SIZE,
		.func	= flash_erase_incomplete_unit,
		.ll_unit_func	= G_qspi_flash_blockerase64k,
	},
};

int gkt_flash_erase(uint32_t dev_id, 
			uint32_t offset, uint32_t size)
{
	const flash_erase_attr_s *c_attr;
	uint32_t curr_offset, left_size, incomplete_size, i;
	int retval;

	if (flash_check_base_params(dev_id, offset, size)) {
		curr_offset = offset;
		left_size = size;

		i = FLASH_ERASE_INCOMPLETE_SECTOR;
		while (i < FLASH_ERASE_MAX) {
			c_attr = &sc_flash_erase_attrs[i];
			if (left_size < c_attr->ll_unit_size)
				break;

			if (curr_offset % c_attr->unit_size) {
				incomplete_size = c_attr->unit_size - (curr_offset % c_attr->unit_size);
				if (incomplete_size > left_size)
					incomplete_size = left_size;
				else if ((i > FLASH_ERASE_INCOMPLETE_SECTOR)
					&& (left_size < (incomplete_size + c_attr->unit_size)))
				{
					incomplete_size = left_size;
				}

				retval = (*c_attr->func)(c_attr, curr_offset, incomplete_size);
				if (retval < GKT_SUCCESS)
					return retval;

				curr_offset += (uint32_t)retval;
				left_size -= (uint32_t)retval;
				if (!left_size)
					return GKT_SUCCESS;
			}

			i++;
		}

		while (i > 0) {
			c_attr = &sc_flash_erase_attrs[--i];
			if (left_size < c_attr->ll_unit_size)
				continue;

			incomplete_size = c_attr->unit_size - (curr_offset % c_attr->unit_size);
			if (incomplete_size > left_size)
				incomplete_size = left_size;

			retval = (*c_attr->func)(c_attr, curr_offset, incomplete_size);
			if (retval < GKT_SUCCESS)
				return retval;

			curr_offset += (uint32_t)retval;
			left_size -= (uint32_t)retval;
			if (!left_size)
				return GKT_SUCCESS;
		}
	}

	return GKT_EPARAM;
}

int gkt_flash_erase_chip(uint32_t dev_id)
{
	return GKT_ENOTSUPPORT;
}

int gkt_flash_write_protect(uint32_t dev_id,
			uint32_t offset, uint32_t size)
{
	return GKT_ENOTSUPPORT;
}

int gkt_flash_init(void)
{
	return GKT_SUCCESS;
}
GKT_DEVICE_DESC_DEFINE(flash, NORMAL);

